package com.example.port.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * JWT工具类
 */
@Component
public class JwtUtils {
    
    // 确保密钥长度至少为256位（32字节）
    private final SecretKey key = Keys.hmacShaKeyFor(
            Constants.JWT.SECRET_KEY.getBytes(StandardCharsets.UTF_8)
    );
    
    /**
     * 从Token中提取用户名
     *
     * @param token JWT Token
     * @return 用户名
     * @throws ExpiredJwtException 如果Token已过期
     */
    public String extractUsername(String token) throws ExpiredJwtException {
        return extractClaim(token, Claims::getSubject);
    }
    
    /**
     * 从Token中提取过期时间
     *
     * @param token JWT Token
     * @return 过期时间
     * @throws ExpiredJwtException 如果Token已过期
     */
    public Date extractExpiration(String token) throws ExpiredJwtException {
        return extractClaim(token, Claims::getExpiration);
    }
    
    /**
     * 从Token中提取特定声明
     *
     * @param token          JWT Token
     * @param claimsResolver 声明解析器
     * @param <T>            声明类型
     * @return 声明值
     * @throws ExpiredJwtException 如果Token已过期
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) throws ExpiredJwtException {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    /**
     * 从Token中提取所有声明
     *
     * @param token JWT Token
     * @return 所有声明
     * @throws ExpiredJwtException 如果Token已过期
     */
    private Claims extractAllClaims(String token) throws ExpiredJwtException {
        return Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
    
    /**
     * 检查Token是否已过期
     *
     * @param token JWT Token
     * @return 是否已过期
     */
    private Boolean isTokenExpired(String token) {
        try {
            return extractExpiration(token).before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
    
    /**
     * 生成Token
     *
     * @param userDetails 用户详情
     * @return JWT Token
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }
    
    /**
     * 使用用户名生成Token
     *
     * @param username 用户名
     * @return JWT Token
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }
    
    /**
     * 使用User对象生成Token
     *
     * @param user 用户对象
     * @return JWT Token
     */
    public String generateToken(com.example.port.entity.User user) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, user.getUsername());
    }
    
    /**
     * 创建Token
     *
     * @param claims   声明
     * @param subject  主题（用户名）
     * @return JWT Token
     */
    private String createToken(Map<String, Object> claims, String subject) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + Constants.JWT.EXPIRATION_TIME);
        
        return Jwts.builder()
                .claims(claims)
                .subject(subject)
                .issuedAt(now)
                .expiration(expiryDate)
                .signWith(key)
                .compact();
    }
    
    /**
     * 验证Token是否有效
     *
     * @param token       JWT Token
     * @param userDetails 用户详情
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        try {
            final String username = extractUsername(token);
            return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        } catch (ExpiredJwtException e) {
            // Token已过期
            return false;
        } catch (JwtException e) {
            // 其他JWT异常
            return false;
        }
    }
}
